#include "xmem_utility.h"

namespace xge{

  void* MemoryPool::Buy() {
    if( next_ == NULL ) {
      return NULL;
    }
    void* ret = next_ ;
    next_ = next_->next_addr;
    ++alloc_size_;
    --residual_size_;
    return ret;
  }


  void MemoryPool::Sell( void* ptr ) {
    XASSERT_SAY(alloc_size_!=0,_xT("There must be some error in memory pool ,")
      _xT("may be sell not managed memory block into the pool!"));
    ((FreeList*)(ptr))->next_addr = next_;
    next_ = (FreeList*)(ptr);
    --alloc_size_;
    ++residual_size_;
  }

  void MemoryPool::Grow( size_t ratio ) {

    // We will get a block of big memory which size is 
    // ratio* reserve_size_ * Padding(sizeof(T),AlignmentSize) + sizeof( LinkedList ) in byte
    // Layout:
    // |-----------|
    // |  header   |
    // |-----------|
    // |  memory   |
    // |           |
    // |-----------|
    // After alignment, each element size
    const size_t type_size = Padding(object_size_,alignment_size_);
    // the whole block size in byte
    residual_size_+=ratio*base_reserve_size_;
    const size_t this_raw_size = ratio*base_reserve_size_*type_size+sizeof(LinkedList);
    // adjust the residual size 
    // malloc the memory
    void* raw_memory = XMalloc( this_raw_size );
    typedef unsigned char byte_t;
    // Now adjust the memory header 
    LinkedList* this_block = (LinkedList*)(raw_memory);
    this_block->block_size = this_raw_size;
    this_block->next_block = NULL;
    // This is the same as
    // (byte_t*)(this_block) + sizeof(LinkedList) 
    void* pool_memory = (void*)( this_block + 1 );
    // here we have just adjust he memory 
    // we will push the memory into the freelist now

    const size_t block_nums = base_reserve_size_ * ratio ;
    FreeList* cur = (FreeList*)(pool_memory);
    FreeList* last= (FreeList*)(pool_memory);
    for( size_t i = 1 ; i < block_nums ; ++i ) {
      // move the current pointer of memory to the next type_size byte
      cur = (FreeList*)( (byte_t*)(cur) + type_size );
      last->next_addr = cur;
      last= cur;
    }
    last->next_addr = NULL;

    // Now do the linking stuff
    if( next_ == NULL ) {
      next_ = (FreeList*)(pool_memory);
    } else {
      last->next_addr = next_;
      next_ = (FreeList*)(pool_memory);
    }

    if( block_ptr_ == NULL ) {
      block_ptr_ = this_block;
    } else {
      block_ptr_->next_block = this_block;
    }
  }

  bool MemoryPool ::Clear( bool force /*false*/ ) {
    if( !force && alloc_size_ != 0 ) {
      return false;
    }
    while( block_ptr_ != NULL ) {
      LinkedList* next = block_ptr_->next_block;
      XFree(block_ptr_);
      block_ptr_ = next;
    }
    alloc_size_= 0;
    block_ptr_ = NULL;
    residual_size_ = 0;
    next_= NULL;
    return true;
  }


}// namespace xge